﻿using BaseUtils.AppConfig;
using BaseUtils.Helper;
using BaseUtils.Logger;
using BaseUtils.Utils;
using S7.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace DeviceManage.PLC
{

    public delegate void ErrorHandler(PLCHelper PLC, string AddressOffset, Exception ex);

    public class PLCHelper : IPLCHelper
    {

        //const string IpAddress = "192.168.0.10";
        public PlcInfo Config;
        public ErrorCode _ErrorCode;
        private bool _LinkSucceed = false;
        public bool LinkSucceed
        {
            get { return _LinkSucceed; }
            set { _LinkSucceed = value; }
        }

        public event ErrorHandler OnErrorHandler;

        SiemensS7 S7PLC;

        public static List<Type> WriteTypes = new List<Type>
        {
            typeof(int),
            typeof(uint),
            typeof(byte),
            typeof(long),
            typeof(float),
            typeof(double),
            typeof(bool),
            typeof(string),
            typeof(ushort)
        };

        static object lockObj = new object();

        public void Link()
        {
            lock (lockObj)
            {
                if (CommonUtil.PingTry(Config.Ip))
                {
                    S7PLC = new SiemensS7(Config.cpuType.ToEnum<CpuType>(), Config.Ip, Config.Port, (short)Config.CpuSolt);
                    LinkSucceed = S7PLC.plc.IsConnected;
                    LoggerService.Info($"PLC {Config.Ip} : " + (LinkSucceed ? "连接成功" : "连接失败"));
                }
                else
                {
                    Error(ErrorCode.ConnectionError,"",new Exception($"PLC {Config.Ip} : 连接失败"));
                }
            }
        }

        public bool Successed()
        {
            return LinkSucceed;
        }

        /// <summary>
        /// 读取PLC值，自动判断类型
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public object Read(string OffsetAddress)
        {
            string[] Array = OffsetAddress.Split('.');
            string BataType = Array[1].Substring(0, 3);
            object obj = null;
            switch (BataType)
            {
                case "DBB": obj = Read<byte>(OffsetAddress); break;
                case "DBW": obj = Read<ushort>(OffsetAddress); break;
                case "DBD": obj = Read<uint>(OffsetAddress); break;
                case "DBF": obj = Read<float>(OffsetAddress); break;
                case "DBS": obj = Read<string>(OffsetAddress); break;
                case "DBX": obj = Read<bool>(OffsetAddress); break;
                default:
                    Error(ErrorCode.IPAddressNotAvailable, OffsetAddress, new Exception("填写的DB数据格式错误，DBX\\DBB\\DBW\\DBD\\DBF\\DBS"));
                    break;
            }
            return obj;
        }

        public T Read<T>(string OffsetAddress)
        {
            if (!CheckPLC(OffsetAddress) || !LinkSucceed)
            {
                return default(T);
            }
            lock (lockObj)
            {
                if (!string.IsNullOrWhiteSpace(OffsetAddress))
                {
                    try
                    {
                        var obj = S7PLC.ReadPlc(OffsetAddress);
                        if (obj == null)
                        {
                            return default(T);
                        }
                        var value = (T)obj;
                        return value;
                    }
                    catch (Exception ex)
                    {
                        Error(ErrorCode.ReadData, OffsetAddress, ex);
                    }
                }
            }
            return default(T);
        }

        private void Error(ErrorCode Code, string OffsetAddress, Exception ex)
        {
            bool IsConnected = CommonUtil.PingTry(Config.Ip);
            LoggerService.Error($"访问PLC地址失败:{OffsetAddress}，ErrorCode={Code}，原因：{ex.Message}", ex);
            if (!IsConnected || !S7PLC.plc.IsConnected)
            {
                LoggerService.Error($"PLC连接失败或已断开，请检查后重试！");
                _LinkSucceed = false;
                _ErrorCode = ErrorCode.ConnectionError;
            }
            OnErrorHandler?.Invoke(this, OffsetAddress, ex);
        }

        public void WriteType<T>(string OffsetAddress, T value)
        {
            if (!CheckPLC(OffsetAddress) || !LinkSucceed)
            {
                return;
            }
            lock (lockObj)
            {
                if (WriteTypes.Any(t => t == value.GetType()))
                {

                    if (!string.IsNullOrWhiteSpace(OffsetAddress))
                    {
                        try
                        {
                            S7PLC.Write(OffsetAddress, value);
                        }
                        catch (Exception ex)
                        {
                            Error(ErrorCode.WriteData, OffsetAddress, new Exception("填写的DB数据格式错误，DBX\\DBB\\DBW\\DBD\\DBF\\DBS"));
                        }
                    }
                }
            }
        }

        public void Write(string OffsetAddress, object value)
        {
            if (value == null)
            {
                LoggerService.Error($"写入PLC设备值不能为空，地址：{OffsetAddress}");
                return;
            }
            string[] Array = OffsetAddress.Split('.');
            string BataType = Array[1].Substring(0, 3);
            switch (BataType)
            {
                case "DBB": WriteType<byte>(OffsetAddress, byte.Parse(value.ToString())); break;
                case "DBW": WriteType<ushort>(OffsetAddress, ushort.Parse(value.ToString())); break;
                case "DBD": WriteType<uint>(OffsetAddress, uint.Parse(value.ToString())); break;
                case "DBF": WriteType<float>(OffsetAddress, float.Parse(value.ToString())); break;
                case "DBS": WriteType<string>(OffsetAddress, value.ToString()); break;
                case "DBX":
                    if (value == null || string.IsNullOrEmpty(value.ToString()))
                    {
                        LoggerService.Warning($"PLC写BOOL值失败，原因：值异常。地址：{OffsetAddress},值：{value}");
                        return;
                    }
                    WriteType<bool>(OffsetAddress, bool.Parse(value.ToString()));
                    break;
                default:
                    Error(ErrorCode.IPAddressNotAvailable, OffsetAddress, new Exception("填写的DB数据格式错误，DBX\\DBB\\DBW\\DBD\\DBF\\DBS"));
                    break;
            }
        }

        public bool VerificationValue(object Value, string TargetType)
        {
            return false;
        }

        public int ReadClass(object obj, int db, int startAddr)
        {
            if (!CheckPLC(db.ToString()))
            {
                return -1;
            }
            return S7PLC.ReadClass(obj, db, startAddr);
        }

        /// <summary>
        /// todo
        /// </summary>
        /// <param name="Address"></param>
        /// <returns></returns>
        private static bool CheckPLC(string Address)
        {
            return true;
        }
    }
}
